Creación de una visualización de datos storytelling (PEC3)

Descripción y enunciado

El objetivo de esta prueba de evaluación continua es que el estudiante sea capaz de crear una narrativa de datos con técnicas de visualización, es decir, una visualización tipo storytelling. Hans Rossling, unos de los líderes de narrativa de datos, en uno de sus famosos videos comenta: “Tener los datos no es suficiente, hay que mostrarlos de forma que la gente los disfrute y los entienda”.

Contenidos en Netflix 2021: Análisis del crecimiento y la distribución de los contenidos

Hoy en día, Netflix es una de las plataformas más usadas para ver contenido online, es por eso, que vamos a investigar en que momento empezó realmente a ser más conocida, y acercándonos a la actualidad (2023), las poblaciones que la consumen más.

# Importamos las librerías necesarios
library(plotly)
## Loading required package: ggplot2
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.3     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ lubridate 1.9.3     ✔ tibble    3.2.1
## ✔ purrr     1.0.1     ✔ tidyr     1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks plotly::filter(), stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(scales)
## 
## Attaching package: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
library(lubridate)
library(visdat)
library(igraph)
## 
## Attaching package: 'igraph'
## 
## The following objects are masked from 'package:lubridate':
## 
##     %--%, union
## 
## The following objects are masked from 'package:dplyr':
## 
##     as_data_frame, groups, union
## 
## The following objects are masked from 'package:purrr':
## 
##     compose, simplify
## 
## The following object is masked from 'package:tidyr':
## 
##     crossing
## 
## The following object is masked from 'package:tibble':
## 
##     as_data_frame
## 
## The following object is masked from 'package:plotly':
## 
##     groups
## 
## The following objects are masked from 'package:stats':
## 
##     decompose, spectrum
## 
## The following object is masked from 'package:base':
## 
##     union
library(networkD3)

Importación de los datos

El conjunto de datos netflix_titles.xlsx se encuentra disponible en la plataforma Kaggle: https://www.kaggle.com/datasets/shivamb/netflix-shows

Este conjunto de datos tabulares consiste en listados de las películas y programas de televisión disponibles en Netflix, junto con detalles como el reparto, los directores, las puntuaciones, el año de estreno, la duración, etc.

Las variables que presenta el conjunto de datos son: + show_id: identificador único para cada película o serie de Netflix. + type: identificador película o serie (Movie o TV Show). + title: título del contenido. + director: director del contenido. + cast: actores involucrados en el contenido. + country: país donde se produjo el contenido. + date_added: fecha en la que fue añadido el contenido en Netflix. + release_year: año actual del lanzamiento del contenido. + rating: rating televisivo del contenido. + duration: duración total en minutos o nº de temporadas. + listed_in: género. + description: resumen descriptivo del contenido.

# Importamos el conjunto de datos y vemos la estructura que tiene
library(readxl)
netflix_titles <- read_excel("~/Documents/AAESTUDIOS/UOC_Máster_Data_Science/4t_Semestre/Visualización_datos/PEC3/netflix_titles.xlsx", 
    skip = 1)
View(netflix_titles)

# Resumen de las variables del conjunto de datos
glimpse(netflix_titles)
## Rows: 8,807
## Columns: 12
## $ show_id      <chr> "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s1…
## $ type         <chr> "Movie", "TV Show", "TV Show", "TV Show", "TV Show", "TV …
## $ title        <chr> "Dick Johnson Is Dead", "Blood & Water", "Ganglands", "Ja…
## $ director     <chr> "Kirsten Johnson", NA, "Julien Leclercq", NA, NA, "Mike F…
## $ cast         <chr> NA, "Ama Qamata, Khosi Ngema, Gail Mabalane, Thabang Mola…
## $ country      <chr> "United States", "South Africa", NA, NA, "India", NA, NA,…
## $ date_added   <chr> "September 25, 2021", "September 24, 2021", "September 24…
## $ release_year <dbl> 2020, 2021, 2021, 2021, 2021, 2021, 2021, 1993, 2021, 202…
## $ rating       <chr> "PG-13", "TV-MA", "TV-MA", "TV-MA", "TV-MA", "TV-MA", "PG…
## $ duration     <chr> "90 min", "2 Seasons", "1 Season", "1 Season", "2 Seasons…
## $ listed_in    <chr> "Documentaries", "International TV Shows, TV Dramas, TV M…
## $ description  <chr> "As her father nears the end of his life, filmmaker Kirst…

Limpieza de datos

Visualizamos los missing values y los limpiamos

# Visualizamos los missing values y los limpiamos 
vis_miss(netflix_titles, sort_miss = T, cluster = T)

mode <- function(x){
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

#Eliminamos los missings y demostramos que ya no hay
netflix_titles <- netflix_titles %>% 
  replace_na(list(country = mode(netflix_titles$country), rating = mode(netflix_titles$rating), 
                  director = "Unknown", cast = "Unknown", 
                  date_added = mode(netflix_titles$date_added)))
vis_miss(netflix_titles)

anyDuplicated(netflix_titles)
## [1] 0

Transformación del conjunto de datos

Cambio de tipos de datos: “type” a factor y “date_added” a datetime

Agregación de columnas: “year_added”, “main_country”, “main_cast”, “target_age”, “genre”

# Obtenemos nuevas columnas a partir de country, cast y listed_in
# También se cambian las etiquetas de la clasificación a 4 categorías distintas
netflix_titles <- netflix_titles %>%
  mutate(main_country = map(str_split(country, ", "), 1), 
         main_cast = map(str_split(cast, ", "), 1), 
         genre = map(str_split(listed_in, ", "), 1)) %>% 
  unnest(cols = c(main_country, main_cast, genre)) %>% 
  mutate(type = as.factor(type), 
         date_added = mdy(date_added),
         year_added = year(date_added),
         main_country = str_remove(main_country, ","),
         target_age = factor(sapply(rating, switch, 
                             'TV-PG' = 'Older Kids', 
                             'TV-MA' = 'Adults', 
                             'TV-Y7-FV' = 'Older Kids',
                             'TV-Y7' = 'Older Kids',
                             'TV-14' = 'Teens',
                             'R' = 'Adults',
                             'TV-Y' = 'Kids',
                             'NR' = 'Adults',
                             'PG-13' = 'Teens',
                             'TV-G' = 'Kids',
                             'PG' = 'Older Kids',
                             'G' = 'Kids',
                             'UR' = 'Adults',
                             'NC-17' = 'Adults'), level = c("Kids", "Older Kids", "Teens", "Adults"))
         ) 
head(netflix_titles)
## # A tibble: 6 × 17
##   show_id type    title    director cast  country date_added release_year rating
##   <chr>   <fct>   <chr>    <chr>    <chr> <chr>   <date>            <dbl> <chr> 
## 1 s1      Movie   Dick Jo… Kirsten… Unkn… United… 2021-09-25         2020 PG-13 
## 2 s2      TV Show Blood &… Unknown  Ama … South … 2021-09-24         2021 TV-MA 
## 3 s3      TV Show Ganglan… Julien … Sami… United… 2021-09-24         2021 TV-MA 
## 4 s4      TV Show Jailbir… Unknown  Unkn… United… 2021-09-24         2021 TV-MA 
## 5 s5      TV Show Kota Fa… Unknown  Mayu… India   2021-09-24         2021 TV-MA 
## 6 s6      TV Show Midnigh… Mike Fl… Kate… United… 2021-09-24         2021 TV-MA 
## # ℹ 8 more variables: duration <chr>, listed_in <chr>, description <chr>,
## #   main_country <chr>, main_cast <chr>, genre <chr>, year_added <dbl>,
## #   target_age <fct>

Visualización: “Crecimiento del nº de contenidos en Netflix por año”

La siguiente visualización muestra el crecimiento de los contenidos de Netflix según los años, en este caso des de 2008 hasta 2021. En el gráfico, se observa el cambio de recuentos de forma acumulativa por cada contenido, ya sea película (Movie) o serie de televisión (TV SHow).

# Función para mostrar de manera acumulada e interactiva
accumulate_by <- function(dat, var) {
  var <- lazyeval::f_eval(var, dat)
  lvls <- plotly:::getLevels(var)
  dats <- lapply(seq_along(lvls), function(x) {
    cbind(dat[var %in% lvls[seq(1, x)], ], frame = lvls[[x]])
  })
  dplyr::bind_rows(dats)
}

# Transformaciones para acumular los contenidos de cada año
mtv_growth <- netflix_titles %>% 
  group_by(year_added, type) %>% 
  summarise(movie_count = n()) %>% 
  ungroup() %>% 
  mutate(cumulative_count = ave(movie_count, type, FUN = cumsum)) %>% 
  accumulate_by(~year_added) %>% 
  ggplot(aes(x = year_added, y = cumulative_count, color = type, frame = frame)) +
  geom_line(size = 1.5, alpha = 0.8)+
  geom_point(size = 3.5, shape = 21, fill = "white", aes(text = paste0("Year: ", year_added, "<br>",
                                                   "Content Count: ", cumulative_count))) + 
  scale_color_manual(values = c("firebrick", "grey16")) +
  scale_y_continuous(labels = scales::comma) +
  labs(title = "Growth Numbers of Movies and TV Shows by Year",
       x = "",
       y = "Number of Movies/TV Shows",
       color = "",
       ) +
  theme_minimal() +
  theme(title = element_text(face = "bold"))
## `summarise()` has grouped output by 'year_added'. You can override using the
## `.groups` argument.
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning in geom_point(size = 3.5, shape = 21, fill = "white", aes(text =
## paste0("Year: ", : Ignoring unknown aesthetics: text
# Gráfico interactivo con la animación
ggplotly(mtv_growth, tooltip = c("text", "frame")) %>%
  config(displayModeBar = F)

En esta visualización se muestran los contenidos de la plataforma de Netflix des de 2008 hasta 2021, lo que nos permite ver un aumento bastante notório a partir del año 2015 hasta la actualidad, sobretodo en contenido de películas.

Visualización: “Distribución de contenidos de Netflix en diferentes países”

Usamos el mapa cloropleth para diferenciar la distribución de la cantidad de contenidos de la plataforma de Netflix por país.

# Importamos los datos del mapa ggplot2
library(maps)
## 
## Attaching package: 'maps'
## The following object is masked from 'package:purrr':
## 
##     map
mapdata <- map_data("world")


# Cambiamos las etiquetas de varíos países
netflix_for_map <- netflix_titles %>% 
  mutate(main_country = str_replace_all(main_country, 
                             c("United States" = "USA",
                               "United Kingdom" = "UK",
                               "Hong Kong" = "China",
                               "Soviet Union" = "Russia",
                               "West Germany" = "Germany")))


# Contabilizamos y acumulamos el nº de contenidos por cada país
count_country <- netflix_for_map %>% 
  group_by(main_country) %>% 
  summarise(content_count = n()) %>% 
  ungroup() %>% 
  arrange(desc(content_count))


# Unimos los datos del mapa y los datos utilitzados para que coincidan
map_join <- mapdata %>% 
  left_join(. , count_country, by = c("region"="main_country")) %>% 
  mutate(content_count = replace_na(content_count, 0))


# Generamos el gráfico con ggplot() y plotly para hacer el gráfico interactivo
temp <- ggplot() +
  geom_polygon(data = map_join, 
               aes(fill = content_count, x = long, y = lat, 
                   group = group, 
                   text = paste0(region, "<br>",
                                 "Netflix Contents: ", content_count)),
               size = 0, alpha = .9, color = "black"
               ) + 
  labs(title = "Distribution of Netflix Contents by Country") +
  theme_void() +
  scale_fill_gradient(name = "Content Count", 
                      trans = "pseudo_log",
                      breaks = c(0, 7, 56, 403, 3000),
                      labels = c(0, 7, 56, 403, 3000),
                      low =  "bisque2",
                      high = "#b20710") +
  theme(panel.grid.major = element_blank(),
        axis.line = element_blank(),
        plot.title = element_text(face = "bold")) 
## Warning in geom_polygon(data = map_join, aes(fill = content_count, x = long, :
## Ignoring unknown aesthetics: text
ggplotly(temp, tooltip = "text") %>% 
  config(displayModeBar = F) %>% 
  layout(legend = list(x = .1, y = .9))

En el gráfico generado se puede observar como Estados Unidos, con un total de 4.042, es el país que más contenidos produce en la plataforma de Netflix, hecho que tiene sentido, ya que, la plataforma se creó en este país.

También podemos observar, como India es el segundo país con más contenidos en la plataforma, distribuyendo un total de 1008 contenidos distintos.

Conclusión

En conclusión, los datos explotados de la plataforma de Netflix pueden ser explorados en mayor profundidad, ya que, contienen mucha información. En este ejercicio se ha explorado el crecimiento de los contenidos a lo largo de los años y la distribución de los contenidos según los países de todo el mundo.

Hemos podido comprobar como los contenidos de Netflix aumentaron significativamente a partir del año 2015, y que actualmente (o según los datos, hasta 2021) siguen creciendo de manera lineal. Por otro lado, también hemos podido reforzar que estos contenidos derivan principalmente de Estados Unidos y India, ya que contienen un elevado número de contenidos en comparación con el resto de paises.